home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BMUG Revelations
/
BMUG Revelations.toast
/
Utilities
/
Random
/
Confusion
/
source
/
Confusion.c
next >
Wrap
Text File
|
1992-09-18
|
14KB
|
578 lines
/**********
* Sound Confusion
*
* by Bernie Bernstein
* 9/11/92
***********/
#include <GestaltEqu.h>
#include <Memory.h>
#include <OSEvents.h>
#include <Sound.h>
#include <SoundInput.h>
#include <StdIO.h>
#include <Types.h>
#include "Confusion.h"
/* how much memory should the program reserve after making the buffers? */
#define kExtraMem 0x30000
/*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
pointing at a SoundHeader Record and not an 'snd ' resource header. */
#define kHeaderSize 20
typedef struct {
short OnOff;
short Level;
short Length;
} Level;
Boolean inStereo;
short gThreshold;
long gBuffSize;
SCGlobals *globs;
BufInfo gExtraBuffer;
/***
* BufPlay
* This routine takes an snd buffer and a sound channel and turns the information
* into a bufferCmd to the channel.
***/
void BufPlay (Handle Buf, SndChannelPtr channel)
{
SndCommand localSndCmd;
OSErr err;
localSndCmd.cmd = bufferCmd;
localSndCmd.param1 = 0;
localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
err = SndDoCommand (channel, &localSndCmd, false);
if (err != noErr)
AlertUser(err, iPlaying);
return;
}
/***
* SetUpSounds
* SetUpSounds is a routine which takes a buffer handle, sound headers size and
* sample rate value and turns it into a snd buffer with the proper header.
* It then returns the buffer handle back to the caller.
***/
Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
{
OSErr err;
short dummy;
Buf = NewHandle(gBuffSize);
if ((err = MemError()) != noErr || Buf == nil)
AlertUser(err, iSetupBuffer);
MoveHHi (Buf);
if ((err = MemError()) != noErr)
AlertUser(err, iSetupBuffer);
HLock (Buf);
if ((err = MemError()) != noErr)
AlertUser(err, iSetupBuffer);
/* The call to SetupSndHeader is done twice in order to make sure that the
Header size is set correctly. */
err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
if (err != noErr)
AlertUser(err, iSetupBuffer);
err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, gBuffSize - *HeaderSize, &dummy);
if (err != noErr)
AlertUser(err, iSetupBuffer);
return (Buf);
}
/***
* RndRange
* Pick a random number from min to max.
***/
short RndRange(short min, short max)
{
unsigned short r = (unsigned short)Random();
short d = max-min+1;
return r / (0x10000/d) + min;
}
/***
* PickPlayableBuffer
* Return the index of a buffer which is ready to be played. It randomly
* picks one from the playable buffers, that is, ones which have some
* sound in them.
***/
short PickPlayableBuffer(BufInfo *buffers, short max)
{
short rnd;
short i;
for(rnd = RndRange(0,max-1), i=0;
!buffers[rnd].playable;
i++)
{
if (i>100)
return max+1;
rnd = RndRange(0,max-1);
}
return rnd;
}
/***
* PickReadableBuffer
* Return the index of a buffer which can read some new sounds
* First it fills up each buffer, then it randomly picks new ones
* once they are all full.
***/
short PickReadableBuffer(BufInfo *buffers, short max)
{
short rnd;
short i;
for(i=0; i<max; i++)
{
if (!buffers[i].playable)
return i;
}
return RndRange(0,max-1);
}
#define Abs(x) ((x<0)?-(x):x)
#define Min(x,y) ((x<y)?(x):(y))
#define Max(x,y) ((x<y)?(y):(x))
#define BlankSpace 0x1000
/***
* TrimBuffer
* If the buffer is silent, then return false. Otherwise, reduce the
* buffer to just the parts that have sound.
***/
long TrimBuffer(Handle buffer, long headerlen)
{
Boolean result = false;
long oldBuffSize = GetHandleSize(buffer);
long oldEnd = 0;
long oldStart = headerlen;
long newIndex = 0;
long copySize = 0;
long bufByte = 0;
Boolean firstTime = true;
Handle tmpbuffer = gExtraBuffer.buffer;
/* copy header into new buffer */
BlockMove(*buffer, *tmpbuffer, headerlen);
/* determine if the buffer was loud enough */
/* loud means that a byte in the buffer is far enough away from the */
/* center to get above the gThreshold */
for (bufByte=0; bufByte<oldBuffSize-headerlen; bufByte++)
{
short delta = 0x80 - (unsigned char)(*buffer)[headerlen + bufByte];
if (Abs(delta) << 1 > gThreshold)
{
/* the first time we hear something, save the previous BlankSpace bytes */
/* until now. */
if (firstTime)
{
/* If we are close to the beginning, copy from the beginning. */
/* otherwise, copy blank space until this spot */
if (bufByte > BlankSpace)
oldStart = bufByte - BlankSpace;
else
oldStart = 0;
/* If we are near the end, copy to the end. */
/* otherwise, copy blank space after this spot */
oldEnd = bufByte + BlankSpace;
if (headerlen + oldEnd > oldBuffSize)
oldEnd = oldBuffSize - headerlen;
copySize = oldEnd - oldStart;
BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
newIndex += copySize;
bufByte = oldEnd;
firstTime = false;
}
else
{
/* After the first time, bufByte > BlankSpace. */
oldStart = bufByte - BlankSpace;
if (oldStart < oldEnd)
oldStart = oldEnd+1;
oldEnd = bufByte + BlankSpace;
if (headerlen + oldEnd > oldBuffSize)
oldEnd = oldBuffSize - headerlen;
copySize = oldEnd - oldStart;
BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
newIndex += copySize;
bufByte = oldEnd;
}
result = true;
}
}
if (result)
{
BlockMove(*tmpbuffer, *buffer, newIndex+headerlen);
return newIndex;
}
else
{
return 0L;
}
}
#define TestBit(l,b) ((l>>b)&0x01)
/***
* InitSoundConfusion
* Allocate memory and initialize the sound driver
***/
void InitSoundConfusion(void)
{
Level myLevel;
OSErr err;
long feature;
BufInfo *Buffers;
short i;
short numBuffers;
short meterState;
SPBPtr RecordRec;
Str255 settingStr;
long tmplong;
/* use Gestalt to make sure the app will work */
err = Gestalt(gestaltSoundAttr, &feature);
if (err == noErr)
{
if (!TestBit(feature, gestaltHasSoundInputDevice))
{
AlertUser(0, iNoInput);
ExitToShell();
}
else
inStereo = TestBit(feature, gestaltStereoCapability);
}
else
{
AlertUser(err, iGestaltFailed);
ExitToShell();
}
GetIndString(settingStr, rSettingStrings, iThreshold);
StringToNum(settingStr, &tmplong);
gThreshold = tmplong;
GetIndString(settingStr, rSettingStrings, iBufferSize);
StringToNum(settingStr, &tmplong);
gBuffSize = tmplong;
globs = (SCGlobals*)NewPtr(sizeof(SCGlobals));
if ((err = MemError()) != noErr || globs == nil)
{
AlertUser(err, iStarting);
return;
}
/* Open sound input drive (whichever one is selected in the sound cdev) */
err = SPBOpenDevice (nil, siWritePermission, &globs->SoundRefNum);
if (err != noErr)
{
AlertUser(err, iStarting);
return;
}
// turn on sound metering
meterState = 1; // turn it on
err = SPBSetDeviceInfo(globs->SoundRefNum, siLevelMeterOnOff, (char *)&meterState);
if (err != noErr)
Debugger();
/* Get the sample rate information for the snd header */
err = SPBGetDeviceInfo (globs->SoundRefNum,siSampleRate, (Ptr) &globs->sampleRate);
if (err != noErr)
{
AlertUser(err, iStarting);
return;
}
/* build the RecordRec pointer and fill in the fields */
RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
if ((err = MemError()) != noErr || RecordRec == nil)
{
AlertUser(err, iStarting);
return;
}
/* how many buffers can we make? (leave space for an extra buffer and then some memory) */
numBuffers = ((FreeMem() - kExtraMem) / gBuffSize) - 1;
if (numBuffers < 1)
{
AlertUser(0, iMemory);
return;
}
Buffers = (BufInfo*)NewPtr(sizeof(BufInfo)*numBuffers);
if ((err = MemError()) != noErr || Buffers == nil)
{
AlertUser(err, iStarting);
return;
}
globs->fullBuffer = false;
globs->loudEnough = false;
globs->bufferGettingFilled = 0;
globs->buffers = Buffers;
globs->numBuffers = numBuffers;
globs->RecordRec = RecordRec;
/* build the snd buffers */
for (i=0; i<numBuffers; i++)
{
Buffers[i].buffer = SetUpSounds(Buffers[i].buffer, &Buffers[i].headerlength, globs->sampleRate);
Buffers[i].buffSize = gBuffSize - Buffers[i].headerlength;
Buffers[i].playable = false;
}
gExtraBuffer.buffer = SetUpSounds(gExtraBuffer.buffer, &gExtraBuffer.headerlength, globs->sampleRate);
RecordRec->inRefNum = globs->SoundRefNum;
RecordRec->count = gBuffSize - Buffers[0].headerlength;
RecordRec->milliseconds = 0;
RecordRec->bufferLength = gBuffSize - Buffers[0].headerlength;
RecordRec->bufferPtr = (Ptr) ((*Buffers[0].buffer) + Buffers[0].headerlength);
RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
RecordRec->interruptRoutine = nil;
RecordRec->userLong = (long)globs;
RecordRec->error = 0;
RecordRec->unused1 = 0;
if (inStereo)
{
/* open the sound channel which I will need to play from */
globs->leftChan = nil;
err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
if (err != noErr)
{
AlertUser(err, iStarting);
return;
}
/* open the sound channel which I will need to play from */
globs->rightChan = nil;
err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
if (err != noErr)
{
AlertUser(err, iStarting);
return;
}
}
else
{
/* open the sound channel which I will need to play from */
globs->leftChan = nil;
err = SndNewChannel (&globs->leftChan, sampledSynth, 0, nil);
if (err != noErr)
{
AlertUser(err, iStarting);
return;
}
}
globs->firstTime = true;
}
/***
* ConfuseSound
* This is called during idle time from the event loop.
* It starts recording from a buffer and playing from another one.
***/
void ConfuseSound(void)
{
short dummy;
OSErr err;
BufInfo *Buffers;
short i;
SndCommand mycmd;
short recordStat, MetLevel;
unsigned long totalSampleRecord, TotalMilRecord, ActSampleRecord, ActMilRecord;
long bufByte;
short bufNum;
Buffers = globs->buffers;
/* get the loudness of the current input */
err = SPBGetRecordingStatus(globs->SoundRefNum, &recordStat, &MetLevel, &totalSampleRecord,
&ActSampleRecord, &TotalMilRecord, &ActMilRecord);
if (err != noErr)
AlertUser(err, iReadingLevel);
if (globs->fullBuffer || globs->firstTime)
{
globs->fullBuffer = false;
if (!globs->firstTime)
{
long newSize;
bufNum = globs->bufferGettingFilled;
newSize = TrimBuffer(Buffers[bufNum].buffer, Buffers[bufNum].headerlength);
Buffers[bufNum].playable = globs->loudEnough = (newSize > 0);
Buffers[bufNum].buffSize = newSize;
/* if the buffer was loud enough, then prepare to record the next readable buffer */
if (globs->loudEnough)
{
short nextbuf = PickReadableBuffer(Buffers, globs->numBuffers);
globs->bufferGettingFilled = nextbuf;
globs->loudEnough = false;
Buffers[nextbuf].playable = false;
HUnlock(Buffers[nextbuf].buffer);
SetHandleSize(Buffers[nextbuf].buffer, gBuffSize);
MoveHHi(Buffers[nextbuf].buffer);
HLock(Buffers[nextbuf].buffer);
globs->RecordRec->bufferPtr = (*Buffers[nextbuf].buffer) + Buffers[nextbuf].headerlength;
}
}
globs->firstTime = false;
/* start recording into the buffer asynchronously */
err = SPBRecord (globs->RecordRec, true); // start recording
if (err != noErr)
AlertUser(err, iRecording);
/* pick a buffer to play from */
i = PickPlayableBuffer(Buffers, globs->numBuffers);
if (i<=globs->numBuffers)
{
long bsize = Buffers[i].buffSize;
err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
if (err != noErr)
AlertUser(err, iPlaying);
/* play it asynchronously */
BufPlay(Buffers[i].buffer,
inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
}
}
}
/***
* StopConfusion
* Deallocate memory and close stuff.
***/
void StopConfusion(void)
{
OSErr err;
SndCommand mycmd;
short i;
/* stop playing sounds now */
mycmd.cmd = quietCmd;
mycmd.param1 = 0;
mycmd.param2 = 0;
err = SndDoImmediate (globs->leftChan, &mycmd);
if (err != noErr)
AlertUser(err, iClosing);
if (inStereo)
{
err = SndDoImmediate (globs->leftChan, &mycmd);
if (err != noErr)
AlertUser(err, iClosing);
}
/* kill the channel */
err = SndDisposeChannel (globs->leftChan,false);
if (err != noErr)
AlertUser(err, iClosing);
if (inStereo)
{
err = SndDisposeChannel (globs->rightChan,false);
if (err != noErr)
AlertUser(err, iClosing);
}
/* close the input device */
SPBCloseDevice (globs->SoundRefNum);
/* kill the snd buffers */
for (i=0; i<globs->numBuffers; i++)
{
HUnlock(globs->buffers[i].buffer);
DisposeHandle(globs->buffers[i].buffer);
}
HUnlock(gExtraBuffer.buffer);
DisposeHandle(gExtraBuffer.buffer);
/* kill the buffers */
DisposePtr ((Ptr) globs->buffers);
DisposePtr ((Ptr) globs->RecordRec);
DisposePtr ((Ptr) globs);
}
/**********************************/
void DoAbout()
{
Str31 numBufsStr;
Str31 numFilledStr;
short numFilled = 0;
short i;
short itemHit;
DialogPtr dlg;
for (i=0; i<globs->numBuffers; i++)
{
if (globs->buffers[i].playable)
numFilled++;
}
NumToString((long)globs->numBuffers, numBufsStr);
NumToString((long)numFilled, numFilledStr);
ParamText(numBufsStr, numFilledStr, "", "");
dlg = GetNewDialog(rAboutAlert, NULL, (WindowPtr)-1L);
ModalDialog(NULL, &itemHit);
DisposDialog(dlg);
}
/***
* MyRecComp
*
* This is the Completion Routine which is called every time the buffer,
* which is being recorded into, is full.
***/
pascal void MyRecComp (SPBPtr inParamPtr)
{
SCGlobals *glob;
glob = (SCGlobals*)inParamPtr->userLong;
glob->fullBuffer = true;
return;
}